From ff2fa963720a679e60d968106f1d8938fdc818c0 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 2 Apr 2018 23:39:42 +0300 Subject: [PATCH] Extract target info --- .../cargo_rustc/context/compilation_files.rs | 113 ++------ src/cargo/ops/cargo_rustc/context/mod.rs | 189 +------------ .../ops/cargo_rustc/context/target_info.rs | 263 ++++++++++++++++++ 3 files changed, 292 insertions(+), 273 deletions(-) create mode 100644 src/cargo/ops/cargo_rustc/context/target_info.rs diff --git a/src/cargo/ops/cargo_rustc/context/compilation_files.rs b/src/cargo/ops/cargo_rustc/context/compilation_files.rs index 98217d260..b1cfa97c6 100644 --- a/src/cargo/ops/cargo_rustc/context/compilation_files.rs +++ b/src/cargo/ops/cargo_rustc/context/compilation_files.rs @@ -1,4 +1,4 @@ -use std::collections::hash_map::{Entry, HashMap}; +use std::collections::HashMap; use std::env; use std::fmt; use std::hash::{Hash, Hasher, SipHasher}; @@ -241,25 +241,16 @@ impl<'a, 'cfg: 'a> CompilationFiles<'a, 'cfg> { } else { crate_type }; - let mut crate_types = info.crate_types.borrow_mut(); - let entry = crate_types.entry(crate_type.to_string()); - let crate_type_info = match entry { - Entry::Occupied(o) => &*o.into_mut(), - Entry::Vacant(v) => { - let value = info.discover_crate_type(v.key())?; - &*v.insert(value) - } - }; - match *crate_type_info { - Some((ref prefix, ref suffix)) => { - let suffixes = add_target_specific_suffixes( - cx.target_triple(), - crate_type, - unit.target.kind(), - suffix, - file_type, - ); - for file_type in suffixes { + let file_types = info.file_types( + crate_type, + file_type, + unit.target.kind(), + cx.target_triple(), + )?; + + match file_types { + Some(types) => { + for file_type in types { // wasm bin target will generate two files in deps such as // "web-stuff.js" and "web_stuff.wasm". Note the different usages of // "-" and "_". should_replace_hyphens is a flag to indicate that @@ -274,23 +265,27 @@ impl<'a, 'cfg: 'a> CompilationFiles<'a, 'cfg> { }; let filename = out_dir.join(format!( "{}{}{}", - prefix, + file_type.prefix, conv(file_stem.clone()), file_type.suffix, )); let link_dst = link_stem.clone().map(|(ld, ls)| { - ld.join(format!("{}{}{}", prefix, conv(ls), file_type.suffix)) + ld.join(format!( + "{}{}{}", + file_type.prefix, + conv(ls), + file_type.suffix + )) }); ret.push((filename, link_dst, file_type.target_file_type)); } - Ok(()) } // not supported, don't worry about it None => { unsupported.push(crate_type.to_string()); - Ok(()) } } + Ok(()) }; //info!("{:?}", unit); match *unit.target.kind() { @@ -450,73 +445,3 @@ fn compute_metadata<'a, 'cfg>( } Some(Metadata(hasher.finish())) } - -struct FileType { - suffix: String, - target_file_type: TargetFileType, - should_replace_hyphens: bool, -} - -// (not a rustdoc) -// Return a list of 3-tuples (suffix, file_type, should_replace_hyphens). -// -// should_replace_hyphens will be used by the caller to replace "-" with "_" -// in a bin_stem. See the caller side (calc_target_filenames()) for details. -fn add_target_specific_suffixes( - target_triple: &str, - crate_type: &str, - target_kind: &TargetKind, - suffix: &str, - file_type: TargetFileType, -) -> Vec { - let mut ret = vec![ - FileType { - suffix: suffix.to_string(), - target_file_type: file_type, - should_replace_hyphens: false, - }, - ]; - - // rust-lang/cargo#4500 - if target_triple.ends_with("pc-windows-msvc") && crate_type.ends_with("dylib") - && suffix == ".dll" - { - ret.push(FileType { - suffix: ".dll.lib".to_string(), - target_file_type: TargetFileType::Normal, - should_replace_hyphens: false, - }) - } - - // rust-lang/cargo#4535 - if target_triple.starts_with("wasm32-") && crate_type == "bin" && suffix == ".js" { - ret.push(FileType { - suffix: ".wasm".to_string(), - target_file_type: TargetFileType::Normal, - should_replace_hyphens: true, - }) - } - - // rust-lang/cargo#4490, rust-lang/cargo#4960 - // - only uplift debuginfo for binaries. - // tests are run directly from target/debug/deps/ - // and examples are inside target/debug/examples/ which already have symbols next to them - // so no need to do anything. - if *target_kind == TargetKind::Bin { - if target_triple.contains("-apple-") { - ret.push(FileType { - suffix: ".dSYM".to_string(), - target_file_type: TargetFileType::DebugInfo, - should_replace_hyphens: false, - }) - } else if target_triple.ends_with("-msvc") { - ret.push(FileType { - suffix: ".pdb".to_string(), - target_file_type: TargetFileType::DebugInfo, - should_replace_hyphens: false, - }) - } - } - - ret -} diff --git a/src/cargo/ops/cargo_rustc/context/mod.rs b/src/cargo/ops/cargo_rustc/context/mod.rs index 482d80cb6..f54b8015f 100644 --- a/src/cargo/ops/cargo_rustc/context/mod.rs +++ b/src/cargo/ops/cargo_rustc/context/mod.rs @@ -5,13 +5,12 @@ use std::env; use std::path::{Path, PathBuf}; use std::str::{self, FromStr}; use std::sync::Arc; -use std::cell::RefCell; use jobserver::Client; use core::{Package, PackageId, PackageSet, Profile, Resolve, Target}; use core::{Dependency, Profiles, Workspace}; -use util::{internal, profile, Cfg, CfgExpr, Config, ProcessBuilder}; +use util::{internal, profile, Cfg, CfgExpr, Config}; use util::errors::{CargoResult, CargoResultExt}; use super::TargetConfig; @@ -28,6 +27,10 @@ mod compilation_files; use self::compilation_files::CompilationFiles; pub use self::compilation_files::Metadata; +mod target_info; +pub use self::target_info::TargetFileType; +use self::target_info::TargetInfo; + /// All information needed to define a Unit. /// /// A unit is an object that has enough information so that cargo knows how to build it. @@ -63,17 +66,6 @@ pub struct Unit<'a> { pub kind: Kind, } -/// Type of each file generated by a Unit. -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum TargetFileType { - /// Not a special file type. - Normal, - /// It is something you can link against (e.g. a library) - Linkable, - /// It is a piece of external debug information (e.g. *.dSYM and *.pdb) - DebugInfo, -} - /// The build context, containing all information about a build task pub struct Context<'a, 'cfg: 'a> { /// The workspace the build is for @@ -105,34 +97,6 @@ pub struct Context<'a, 'cfg: 'a> { files: Option>, } -#[derive(Clone, Default)] -struct TargetInfo { - crate_type_process: Option, - crate_types: RefCell>>, - cfg: Option>, - sysroot_libdir: Option, -} - -impl TargetInfo { - fn discover_crate_type(&self, crate_type: &str) -> CargoResult> { - let mut process = self.crate_type_process.clone().unwrap(); - - process.arg("--crate-type").arg(crate_type); - - let output = process.exec_with_output().chain_err(|| { - format!( - "failed to run `rustc` to learn about \ - crate-type {} information", - crate_type - ) - })?; - - let error = str::from_utf8(&output.stderr).unwrap(); - let output = str::from_utf8(&output.stdout).unwrap(); - Ok(parse_crate_type(crate_type, error, &mut output.lines())?) - } -} - impl<'a, 'cfg> Context<'a, 'cfg> { pub fn new( ws: &'a Workspace<'cfg>, @@ -238,109 +202,17 @@ impl<'a, 'cfg> Context<'a, 'cfg> { _ => true, }; - if host_target_same { - let info = self.probe_target_info_kind(Kind::Target)?; - self.host_info = info.clone(); - self.target_info = info; + self.host_info = TargetInfo::new(self, Kind::Host)?; + self.target_info = if host_target_same { + self.host_info.clone() } else { - self.host_info = self.probe_target_info_kind(Kind::Host)?; - self.target_info = self.probe_target_info_kind(Kind::Target)?; - } + TargetInfo::new(self, Kind::Target)? + }; self.compilation.host_dylib_path = self.host_info.sysroot_libdir.clone(); self.compilation.target_dylib_path = self.target_info.sysroot_libdir.clone(); Ok(()) } - fn probe_target_info_kind(&self, kind: Kind) -> CargoResult { - let rustflags = env_args( - self.config, - &self.build_config, - self.info(&kind), - kind, - "RUSTFLAGS", - )?; - let mut process = self.config.rustc()?.process(); - process - .arg("-") - .arg("--crate-name") - .arg("___") - .arg("--print=file-names") - .args(&rustflags) - .env_remove("RUST_LOG"); - - if kind == Kind::Target { - process.arg("--target").arg(&self.target_triple()); - } - - let crate_type_process = process.clone(); - const KNOWN_CRATE_TYPES: &[&str] = - &["bin", "rlib", "dylib", "cdylib", "staticlib", "proc-macro"]; - for crate_type in KNOWN_CRATE_TYPES.iter() { - process.arg("--crate-type").arg(crate_type); - } - - let mut with_cfg = process.clone(); - with_cfg.arg("--print=sysroot"); - with_cfg.arg("--print=cfg"); - - let mut has_cfg_and_sysroot = true; - let output = with_cfg - .exec_with_output() - .or_else(|_| { - has_cfg_and_sysroot = false; - process.exec_with_output() - }) - .chain_err(|| "failed to run `rustc` to learn about target-specific information")?; - - let error = str::from_utf8(&output.stderr).unwrap(); - let output = str::from_utf8(&output.stdout).unwrap(); - let mut lines = output.lines(); - let mut map = HashMap::new(); - for crate_type in KNOWN_CRATE_TYPES { - let out = parse_crate_type(crate_type, error, &mut lines)?; - map.insert(crate_type.to_string(), out); - } - - let mut sysroot_libdir = None; - if has_cfg_and_sysroot { - let line = match lines.next() { - Some(line) => line, - None => bail!( - "output of --print=sysroot missing when learning about \ - target-specific information from rustc" - ), - }; - let mut rustlib = PathBuf::from(line); - if kind == Kind::Host { - if cfg!(windows) { - rustlib.push("bin"); - } else { - rustlib.push("lib"); - } - sysroot_libdir = Some(rustlib); - } else { - rustlib.push("lib"); - rustlib.push("rustlib"); - rustlib.push(self.target_triple()); - rustlib.push("lib"); - sysroot_libdir = Some(rustlib); - } - } - - let cfg = if has_cfg_and_sysroot { - Some(lines.map(Cfg::from_str).collect::>()?) - } else { - None - }; - - Ok(TargetInfo { - crate_type_process: Some(crate_type_process), - crate_types: RefCell::new(map), - cfg, - sysroot_libdir, - }) - } - /// Builds up the `used_in_plugin` internal to this context from the list of /// top-level units. /// @@ -708,44 +580,3 @@ fn env_args( Ok(Vec::new()) } - -/// Takes rustc output (using specialized command line args), and calculates the file prefix and -/// suffix for the given crate type, or returns None if the type is not supported. (e.g. for a -/// rust library like libcargo.rlib, prefix = "lib", suffix = "rlib"). -/// -/// The caller needs to ensure that the lines object is at the correct line for the given crate -/// type: this is not checked. -// This function can not handle more than 1 file per type (with wasm32-unknown-emscripten, there -// are 2 files for bin (.wasm and .js)) -fn parse_crate_type( - crate_type: &str, - error: &str, - lines: &mut str::Lines, -) -> CargoResult> { - let not_supported = error.lines().any(|line| { - (line.contains("unsupported crate type") || line.contains("unknown crate type")) - && line.contains(crate_type) - }); - if not_supported { - return Ok(None); - } - let line = match lines.next() { - Some(line) => line, - None => bail!( - "malformed output when learning about \ - crate-type {} information", - crate_type - ), - }; - let mut parts = line.trim().split("___"); - let prefix = parts.next().unwrap(); - let suffix = match parts.next() { - Some(part) => part, - None => bail!( - "output of --print=file-names has changed in \ - the compiler, cannot parse" - ), - }; - - Ok(Some((prefix.to_string(), suffix.to_string()))) -} diff --git a/src/cargo/ops/cargo_rustc/context/target_info.rs b/src/cargo/ops/cargo_rustc/context/target_info.rs new file mode 100644 index 000000000..17f3ac7e6 --- /dev/null +++ b/src/cargo/ops/cargo_rustc/context/target_info.rs @@ -0,0 +1,263 @@ +use std::cell::RefCell; +use std::collections::hash_map::{Entry, HashMap}; +use std::path::PathBuf; +use std::str::{self, FromStr}; + +use super::{env_args, Context}; +use util::{CargoResult, CargoResultExt, Cfg, ProcessBuilder}; +use core::TargetKind; +use ops::Kind; + +#[derive(Clone, Default)] +pub struct TargetInfo { + crate_type_process: Option, + crate_types: RefCell>>, + pub(super) cfg: Option>, + pub(super) sysroot_libdir: Option, +} + +/// Type of each file generated by a Unit. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum TargetFileType { + /// Not a special file type. + Normal, + /// It is something you can link against (e.g. a library) + Linkable, + /// It is a piece of external debug information (e.g. *.dSYM and *.pdb) + DebugInfo, +} + +pub struct FileType { + pub suffix: String, + pub prefix: String, + pub target_file_type: TargetFileType, + pub should_replace_hyphens: bool, +} + +impl TargetInfo { + pub fn new(cx: &Context, kind: Kind) -> CargoResult { + let rustflags = env_args( + cx.config, + &cx.build_config, + cx.info(&kind), + kind, + "RUSTFLAGS", + )?; + let mut process = cx.config.rustc()?.process(); + process + .arg("-") + .arg("--crate-name") + .arg("___") + .arg("--print=file-names") + .args(&rustflags) + .env_remove("RUST_LOG"); + + if kind == Kind::Target { + process.arg("--target").arg(&cx.target_triple()); + } + + let crate_type_process = process.clone(); + const KNOWN_CRATE_TYPES: &[&str] = + &["bin", "rlib", "dylib", "cdylib", "staticlib", "proc-macro"]; + for crate_type in KNOWN_CRATE_TYPES.iter() { + process.arg("--crate-type").arg(crate_type); + } + + let mut with_cfg = process.clone(); + with_cfg.arg("--print=sysroot"); + with_cfg.arg("--print=cfg"); + + let mut has_cfg_and_sysroot = true; + let output = with_cfg + .exec_with_output() + .or_else(|_| { + has_cfg_and_sysroot = false; + process.exec_with_output() + }) + .chain_err(|| "failed to run `rustc` to learn about target-specific information")?; + + let error = str::from_utf8(&output.stderr).unwrap(); + let output = str::from_utf8(&output.stdout).unwrap(); + let mut lines = output.lines(); + let mut map = HashMap::new(); + for crate_type in KNOWN_CRATE_TYPES { + let out = parse_crate_type(crate_type, error, &mut lines)?; + map.insert(crate_type.to_string(), out); + } + + let mut sysroot_libdir = None; + if has_cfg_and_sysroot { + let line = match lines.next() { + Some(line) => line, + None => bail!( + "output of --print=sysroot missing when learning about \ + target-specific information from rustc" + ), + }; + let mut rustlib = PathBuf::from(line); + if kind == Kind::Host { + if cfg!(windows) { + rustlib.push("bin"); + } else { + rustlib.push("lib"); + } + sysroot_libdir = Some(rustlib); + } else { + rustlib.push("lib"); + rustlib.push("rustlib"); + rustlib.push(cx.target_triple()); + rustlib.push("lib"); + sysroot_libdir = Some(rustlib); + } + } + + let cfg = if has_cfg_and_sysroot { + Some(lines.map(Cfg::from_str).collect::>()?) + } else { + None + }; + + Ok(TargetInfo { + crate_type_process: Some(crate_type_process), + crate_types: RefCell::new(map), + cfg, + sysroot_libdir, + }) + } + + pub fn file_types( + &self, + crate_type: &str, + file_type: TargetFileType, + kind: &TargetKind, + target_triple: &str, + ) -> CargoResult>> { + let mut crate_types = self.crate_types.borrow_mut(); + let entry = crate_types.entry(crate_type.to_string()); + let crate_type_info = match entry { + Entry::Occupied(o) => &*o.into_mut(), + Entry::Vacant(v) => { + let value = self.discover_crate_type(v.key())?; + &*v.insert(value) + } + }; + let (prefix, suffix) = match *crate_type_info { + Some((ref prefix, ref suffix)) => (prefix, suffix), + None => return Ok(None), + }; + let mut ret = vec![ + FileType { + suffix: suffix.to_string(), + prefix: prefix.clone(), + target_file_type: file_type, + should_replace_hyphens: false, + }, + ]; + + // rust-lang/cargo#4500 + if target_triple.ends_with("pc-windows-msvc") && crate_type.ends_with("dylib") + && suffix == ".dll" + { + ret.push(FileType { + suffix: ".dll.lib".to_string(), + prefix: prefix.clone(), + target_file_type: TargetFileType::Normal, + should_replace_hyphens: false, + }) + } + + // rust-lang/cargo#4535 + if target_triple.starts_with("wasm32-") && crate_type == "bin" && suffix == ".js" { + ret.push(FileType { + suffix: ".wasm".to_string(), + prefix: prefix.clone(), + target_file_type: TargetFileType::Normal, + should_replace_hyphens: true, + }) + } + + // rust-lang/cargo#4490, rust-lang/cargo#4960 + // - only uplift debuginfo for binaries. + // tests are run directly from target/debug/deps/ + // and examples are inside target/debug/examples/ which already have symbols next to them + // so no need to do anything. + if *kind == TargetKind::Bin { + if target_triple.contains("-apple-") { + ret.push(FileType { + suffix: ".dSYM".to_string(), + prefix: prefix.clone(), + target_file_type: TargetFileType::DebugInfo, + should_replace_hyphens: false, + }) + } else if target_triple.ends_with("-msvc") { + ret.push(FileType { + suffix: ".pdb".to_string(), + prefix: prefix.clone(), + target_file_type: TargetFileType::DebugInfo, + should_replace_hyphens: false, + }) + } + } + + Ok(Some(ret)) + } + + fn discover_crate_type(&self, crate_type: &str) -> CargoResult> { + let mut process = self.crate_type_process.clone().unwrap(); + + process.arg("--crate-type").arg(crate_type); + + let output = process.exec_with_output().chain_err(|| { + format!( + "failed to run `rustc` to learn about \ + crate-type {} information", + crate_type + ) + })?; + + let error = str::from_utf8(&output.stderr).unwrap(); + let output = str::from_utf8(&output.stdout).unwrap(); + Ok(parse_crate_type(crate_type, error, &mut output.lines())?) + } +} + +/// Takes rustc output (using specialized command line args), and calculates the file prefix and +/// suffix for the given crate type, or returns None if the type is not supported. (e.g. for a +/// rust library like libcargo.rlib, prefix = "lib", suffix = "rlib"). +/// +/// The caller needs to ensure that the lines object is at the correct line for the given crate +/// type: this is not checked. +// This function can not handle more than 1 file per type (with wasm32-unknown-emscripten, there +// are 2 files for bin (.wasm and .js)) +fn parse_crate_type( + crate_type: &str, + error: &str, + lines: &mut str::Lines, +) -> CargoResult> { + let not_supported = error.lines().any(|line| { + (line.contains("unsupported crate type") || line.contains("unknown crate type")) + && line.contains(crate_type) + }); + if not_supported { + return Ok(None); + } + let line = match lines.next() { + Some(line) => line, + None => bail!( + "malformed output when learning about \ + crate-type {} information", + crate_type + ), + }; + let mut parts = line.trim().split("___"); + let prefix = parts.next().unwrap(); + let suffix = match parts.next() { + Some(part) => part, + None => bail!( + "output of --print=file-names has changed in \ + the compiler, cannot parse" + ), + }; + + Ok(Some((prefix.to_string(), suffix.to_string()))) +} -- 2.30.2